/*************************************************************************
 *                                                                       *
 * This file is part of the DogosFrame distribution.  For full copyright *
 * information, see COPYRIGHT and COPYING.LESSER.                        *
 *                                                                       *
 * Author:           sqbang                                              *
 * Email:            sqbang@ustc.edu                                     *
 *                                                                       *
 *************************************************************************/

//declaration header
#include "COMMON/info/InfoManager.h"

#include <iostream>

//COMMON headers
#include "COMMON/base/StateIndicator.h"
#include "COMMON/tbox/InputDatabase.h"
#include "COMMON/tbox/LogMessage.h"
#include "COMMON/info/CommonInfo.h"
#include "COMMON/info/WriterInfo.h"
#include "COMMON/info/MeshInfo.h"
#include "COMMON/info/TimePatch.h"
#include "COMMON/info/AttributeInfo.h"
#include "COMMON/info/RegionInfo.h"


namespace FreeMAPs
{

InfoManager* InfoManager::s_instance (0);



InfoManager::InfoManager():
    d_is_closed (false) ,
    d_common_info (NULL),
    d_writer_info (NULL),
    d_time_patch (NULL),
    d_is_un_steady(true),
    d_is_mesh_info_exist(false),
    d_is_attribute_info_exist(false),
    d_is_region_info_exist(false)
{}



StartupShutdownManager::Handler
InfoManager::s_shutdown_handler(
    0,
    0,
    InfoManager::shutdown_callback,
    0,
    10);

/*
 *************************************************************************
 *                                                                       *
 * Basic singleton classes to create, set, and destroy the manager       *
 * instance.                                                             *
 *                                                                       *
 *************************************************************************
 */

InfoManager*
InfoManager::get_instance()
{
    if (!s_instance)
    {
        s_instance = new InfoManager;
        s_instance->create_objects();
    }

    return s_instance;
}



void
InfoManager::shutdown_callback()
{
    if (s_instance)
    {
        s_instance->clear_all_items();
        delete s_instance;
        s_instance = ((InfoManager*)NULL);
    }
}



void
InfoManager::clear_all_items()
{
    Utilities::clear_pointer_object (d_common_info);
    Utilities::clear_pointer_object (d_writer_info);
    Utilities::clear_pointer_object (d_mesh_info);
    Utilities::clear_pointer_object (d_time_patch);
    Utilities::clear_pointer_object (d_attribute_info);
    Utilities::clear_pointer_object (d_region_info);

    d_all_state_indicators.clear();
    d_mesh_file_name.clear();
}



void
InfoManager::register_state_indicator(
    const StateIndicator* state_indicator) const
{
    FREEMAPS_ASSERT_VALID_POINTER (!d_is_closed && state_indicator);

    if (d_all_state_indicators.has_key (state_indicator) || d_all_state_indicators.has_value (state_indicator->get_full_name()))
    {
        std::cout << "\n full name = \"" << state_indicator->get_full_name() << "\", name = \"" << state_indicator->get_object_name() << "\"" << std::endl;
        FREEMAPS_FIND_BUG();
    }

    d_all_state_indicators.insert (state_indicator, state_indicator->get_full_name());
}



void
InfoManager::close()
{
    d_is_closed = true;
}



bool
InfoManager::check_all_info_db (
        const InputDatabase* db)
{
    //common_info
    if( db->key_exists("common_info") )
    {
        bool check_ok = d_common_info->check_all_info_db(db->get_database("common_info"));
        if( !check_ok )
        {
            return false;
        }
    }
    else
    {
        FREEMAPS_ERROR("in \"base_information\", can not find  \"common_info\" \n");
        return false;
    }

    //writer_info
    if( db->key_exists("writer_info") )
    {
        bool check_ok = d_writer_info->check_all_info_db(db->get_database("writer_info"));
        if( !check_ok )
        {
            return false;
        }
    }
    else
    {
        FREEMAPS_ERROR("in \"base_information\", can not find  \"writer_info\" \n");
        return false;
    }

    //mesh_info
    if( db->key_exists("mesh_info") )
    {
        bool check_ok = d_mesh_info->check_all_info_db(db->get_database("mesh_info"));
        if( !check_ok )
        {
            return false;
        }
    }
    else
    {
        FREEMAPS_ERROR("in \"base_information\", can not find  \"mesh_info\" \n");
        return false;
    }

    //time_info   ---> when steady time_info no need
    if( db->key_exists("time_info") )
    {
        bool check_ok = d_time_patch->check_all_info_db(db->get_database("time_info"));
        if( !check_ok )
        {
            return false;
        }
    }

    //attribute_info
    if(db->key_exists("attribute_info"))
    {
        bool check_ok = d_attribute_info->check_all_info_db(db->get_database("attribute_info"));
        if( !check_ok )
        {
            return false;
        }
    }
    else
    {
        FREEMAPS_ERROR("in \"base_information\", can not find  \"attribute_info\" \n");
        return false;
    }

    //region_info
    if(db->key_exists("region_info"))
    {
        bool check_ok = d_region_info->check_all_info_db(db->get_database("region_info"));
        if( !check_ok )
        {
            return false;
        }
    }
    else
    {
        FREEMAPS_ERROR("in \"base_information\", can not find  \"region_info\" \n");
        return false;
    }

    return true;
}



void
InfoManager::get_from_input (
    const InputDatabase* db)
{
    
    FREEMAPS_ASSERT_VALID_POINTER (db);

    db->insert_allowable_key ("common_info");
    db->insert_allowable_key ("coefficient_info");
    db->insert_allowable_key ("mesh_info");
    db->insert_allowable_key ("writer_info");
    db->insert_allowable_key ("attribute_info");
    db->insert_allowable_key ("refine_info");
    db->insert_allowable_key ("region_info");
    db->insert_allowable_key ("geom_info");
    db->insert_allowable_key ("variable_info");
    db->insert_allowable_key ("function_info");
    db->insert_allowable_key ("time_info");
    db->insert_allowable_key ("debug_info");
    db->insert_allowable_key ("extract_info");

    db->check_allowable_keys ();

    /*
     * build common_info
     */
    {
        FREEMAPS_ASSERT_VALID_POINTER (d_common_info);

        if(db->key_exists("common_info") && db->is_database("common_info"))
        {
            db->check_requisite_key ("common_info", DataType::DATABASE, false, false);
            d_common_info->get_from_input (db->get_database ("common_info"));
        }
    }

    /*
     * build writer_info
     */
     {
        //writer_info is not need now, when read
         FREEMAPS_ASSERT_VALID_POINTER (d_writer_info);
         if(db->key_exists("writer_info") && db->is_database("writer_info"))
         {
             db->check_requisite_database ("writer_info");
             d_writer_info->get_from_input (db->get_database ("writer_info"));
         }
     }

    /*
     * build mesh_info, this should be done before geom_info.
     * because if mesh info use structured mesh, then geom_info can NOT be empty.
     */
    {
        FREEMAPS_ASSERT_VALID_POINTER (d_mesh_info);
        if (db->key_exists ("mesh_info") && db->is_database("mesh_info"))
        {
            d_is_mesh_info_exist = true;
            db->check_optional_key ("mesh_info", DataType::DATABASE, false, false);
            d_mesh_info->get_from_input (db->get_database ("mesh_info"));
        }
        else
        {
            d_is_mesh_info_exist = false;
        }
    }

    /*
     * build time_info
     */
    {
        if (db->key_exists ("time_info") && db->is_database("time_info"))
        {
            //unsteady
            db->check_requisite_database ("time_info");
            d_time_patch->get_from_input(db->get_database("time_info"));
            set_un_steady(true);
        }
        else
        {
            //steady
            set_un_steady(false);
        }
    }

    /*
     * build attribute_info
     */
    {
        FREEMAPS_ASSERT_VALID_POINTER (d_attribute_info);
        if(db->key_exists("attribute_info") && db->is_database("attribute_info"))
        {
            d_is_attribute_info_exist = true;

            db->check_requisite_database ("attribute_info");
            d_attribute_info->get_from_input (db->get_database ("attribute_info"));
        }
        else
        {
            d_is_attribute_info_exist = false;
        }
    }

    /*
     * build region_info
     */
    {
        FREEMAPS_ASSERT_VALID_POINTER (d_region_info);
        if(db->key_exists("region_info") && db->is_database("region_info"))
        {
            d_is_region_info_exist = true;

            db->check_requisite_database ("region_info");
            d_region_info->get_from_input (db->get_database ("region_info"));
        }
        else
        {
            d_is_region_info_exist = false;
        }
    }

}



std::ostream&
operator << (
    std::ostream& os,
    const InfoManager& rhs)
{
    os << "/*********************************************************************" << RTN_FLAG;
    os << " *                                                                   *" << RTN_FLAG;
    os << " * this is a setup file generated automatically, do NOT modified it. *" << RTN_FLAG;
    os << " *                                                                   *" << RTN_FLAG;
    os << " *********************************************************************/"<< RTN_FLAG << std::endl;

    os << "problem_type = \"UNSTRUCTURED_MESH_PROBLEM\"" << RTN_FLAG << std::endl;

    os << "base_information" << RTN_FLAG << "{" << std::endl;

    std::ostringstream oss;

    /*
     * output common_info
     */
    {
        FREEMAPS_ASSERT_VALID_POINTER (rhs.d_common_info);
        oss << *rhs.d_common_info << std::endl;
    }
    /*
     * output writer_info
     */
    {
        FREEMAPS_ASSERT_VALID_POINTER (rhs.d_writer_info);
        oss << RTN_FLAG << *rhs.d_writer_info << std::endl;
    }
    /*
     * output time_info
     */
    {
        if(rhs.is_un_steady())
        {
            FREEMAPS_ASSERT_VALID_POINTER (rhs.d_time_patch);
            oss << RTN_FLAG << *rhs.d_time_patch << std::endl;
        }
    }

    if(rhs.is_mesh_file_name_exist())
    {
        /*
         * output mesh_info
         */
        {
            FREEMAPS_ASSERT_VALID_POINTER (rhs.d_mesh_info);
            oss << RTN_FLAG << *rhs.d_mesh_info << std::endl;
        }
        /*
         * output attribute_info
         */
        {
            FREEMAPS_ASSERT_VALID_POINTER (rhs.d_attribute_info);
            oss << RTN_FLAG << *rhs.d_attribute_info << std::endl;
        }
        /*
         * output region_info
         */
        {
            FREEMAPS_ASSERT_VALID_POINTER (rhs.d_region_info);
            oss << RTN_FLAG << *rhs.d_region_info << std::endl;
        }
    }

    os << LogMessage::get_log_info (oss, ONE_TAB) << std::endl;

    os << "}";

    return os;
}



void
InfoManager::create_objects ()
{
    d_common_info = new CommonInfo ("common_info");
    d_writer_info = new WriterInfo ("writer_info");
    d_mesh_info = new MeshInfo ("mesh_info");
    d_time_patch = new TimePatch ("base_information", "time_info", true); //true means is global
    d_attribute_info = new AttributeInfo ("attribute_info");
    d_region_info = new RegionInfo ("region_info");
}

} //namespace FreeMAPs
